package cc.aiee.zalopay.api;

import java.util.Map;

import com.alibaba.fastjson.JSONObject;

import cc.aiee.core.bean.ReqLog;
import cc.aiee.core0.bean.J;
import cc.aiee.core0.enums.MsgCodeEnum;
import cc.aiee.core0.enums.StatusEnum;
import cc.aiee.core0.kit.KitJson;
import cc.aiee.zalopay.config.ZalopayConfig;
import cc.aiee.zalopay.core.pojo.dto.ZalopayCreateOrderDTO;
import cc.aiee.zalopay.core.pojo.vo.ZalopayCreateOrderVO;
import cc.aiee.zalopay.core.pojo.vo.ZalopayQueryOrderStatusVO;
import cc.aiee.zalopay.core.pojo.vo.ZalopayRefundVO;
import cc.aiee.zalopay.core.pojo.vo.ZalopayVO;
import cc.aiee.zalopay.model.ZalopayCreateOrderModel;
import cc.aiee.zalopay.model.ZalopayQueryOrderStatusModel;
import cc.aiee.zalopay.model.ZalopayQueryRefundModel;
import cc.aiee.zalopay.model.ZalopayRefundModel;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import vn.zalopay.crypto.HMACUtil;

@Slf4j
public class ZalopayApi {

	public J<ReqLog, ZalopayCreateOrderVO> createOrder(ZalopayCreateOrderModel model, ZalopayConfig config) {
		// List<NameValuePair> params = new ArrayList<>();
		ZalopayCreateOrderDTO dto = new ZalopayCreateOrderDTO();
		BeanUtil.copyProperties(model, dto, false);

		if (model.getEmbedData() == null) {
			dto.setEmbed_data("{}");
		} else {
			String embed_data_json = JSONObject.toJSONString(model.getEmbedData());
			dto.setEmbed_data(embed_data_json);
		}

		if (model.getItems() == null) {
			dto.setItem("[{}]");
		} else {
			String item_json_arr = JSONObject.toJSONString(model.getItems());
			dto.setItem(item_json_arr);
		}

		// 添加通知地址
		if (StrUtil.isBlank(model.getCallback_url())) {
			dto.setCallback_url(config.getNotifyUrlPay());
		}

		if (StrUtil.isBlank(dto.getApp_id())) {
			dto.setApp_id(config.getAppId());
		}

		String mac_data = dto.getApp_id() + "|" + dto.getApp_trans_id() + "|" + dto.getApp_user() + "|"
				+ dto.getAmount() + "|" + dto.getApp_time() + "|" + dto.getEmbed_data() + "|" + dto.getItem();
		String mac = HMACUtil.HMacHexStringEncode(HMACUtil.HMACSHA256, config.getKey1(), mac_data);
		dto.setMac(mac);

		Map<String, Object> beanToMap = BeanUtil.beanToMap(dto, false, true);

		String xml = XmlUtil.mapToXmlStr(beanToMap, "OAOrder");

		String url = config.getway("/v2/create");

		ReqLog logreq = new ReqLog(url, xml, null);

		if (log.isDebugEnabled()) {
			log.debug("Zalopay 创建订单;\n{}", KitJson.toJSONString(logreq, true, false));
		}

		String result = HttpUtil.post(url, xml);
		logreq.setResult(result);

		ZalopayCreateOrderVO jsonObj = JSONObject.parseObject(result, ZalopayCreateOrderVO.class);

		if (jsonObj.isSuccessful()) {
			return new J<>(MsgCodeEnum.SUCCESS, jsonObj, logreq);
		}
		return new J<>(MsgCodeEnum.FAIL, jsonObj, logreq);
	}

	public J<ReqLog, ZalopayRefundVO> refund(ZalopayRefundModel model, ZalopayConfig config) {
		model.setAppId(config);
		if (model.getTimestamp() == null) {
			model.setTimestamp(System.currentTimeMillis());
		}
		if (model.getDescription() == null) {
			model.setDescription("Refund");
			model.setMac(config.getKey1(), model.getApp_id(), model.getZp_trans_id(), String.valueOf(model.getAmount()),
					model.getDescription(), String.valueOf(model.getTimestamp()));
		} else {
			model.setMac(config.getKey1(), model.getApp_id(), model.getZp_trans_id(), String.valueOf(model.getAmount()),
					model.getDescription(), String.valueOf(model.getTimestamp()));
		}
		String url = config.getway("/v2/refund");
		Map<String, Object> beanToMap = BeanUtil.beanToMap(model, false, true);
		String xml = XmlUtil.mapToXmlStr(beanToMap);

		ReqLog logreq = new ReqLog(url, xml, null);

		String result = HttpUtil.post(url, xml);
		logreq.setResult(result);

		ZalopayRefundVO jsonObj = JSONObject.parseObject(result, ZalopayRefundVO.class);
		if (jsonObj.isSuccessful()) {
			return new J<>(MsgCodeEnum.SUCCESS, jsonObj, logreq);
		}
		if (jsonObj.getReturnCode() < 1 || jsonObj.getSubReturnCode() < 1) {
			return new J<>(MsgCodeEnum.FAIL, jsonObj, logreq);
		}
		return new J<>(MsgCodeEnum.SUCCESS, StatusEnum.BETWEENNESS, jsonObj, logreq);
	}

	/**
	 * <li>1.单个订单仅在创建后15分钟内有效。</li>
	 * <li>2.从订单创建之日起不到15分钟的时间。在以下情况下，有必要重复API调用过程以获得最终结果<br>
	 * (何时return_code = 3-用户尚未支付订单或交易正在处理中)</li>
	 * <li>订单状态只能在订单建立之日起48小时内查询。如果在48小时后查询，将收到return_code = 3</li>
	 * 
	 * @param model
	 * @param config
	 */
	public J<ReqLog, ZalopayQueryOrderStatusVO> queryOrderStatus(ZalopayQueryOrderStatusModel model,
			ZalopayConfig config) {
		model.setAppId(config);
		model.setMac(config.getKey1(), model.getApp_id(), model.getApp_trans_id(), config.getKey1());

		String url = config.getway("/v2/query");

		Map<String, Object> beanToMap = BeanUtil.beanToMap(model, false, true);
		String xml = XmlUtil.mapToXmlStr(beanToMap);

		ReqLog logreq = new ReqLog(url, xml, null);

		String result = HttpUtil.post(url, xml);
		logreq.setResult(result);

		ZalopayQueryOrderStatusVO jsonObj = JSONObject.parseObject(result, ZalopayQueryOrderStatusVO.class);
		if (jsonObj.isSuccessful()) {
			return new J<>(MsgCodeEnum.SUCCESS, jsonObj, logreq);
		}
		if ("3".equals(jsonObj.getReturn_code()) || Boolean.TRUE.equals(jsonObj.getIs_processing())) {
			return new J<>(MsgCodeEnum.SUCCESS, StatusEnum.BETWEENNESS, jsonObj, logreq);
		}

		return new J<>(MsgCodeEnum.FAIL, jsonObj, logreq);
	}

	public J<ReqLog, ZalopayVO> queryRefundStatus(ZalopayQueryRefundModel model, ZalopayConfig config) {
		model.setAppId(config);
		if (model.getTimestamp() == null) {
			model.setTimestamp(System.currentTimeMillis());
		}
		model.setMac(config.getKey1(), model.getApp_id(), model.getM_refund_id(), String.valueOf(model.getTimestamp()));
		String url = config.getway("/v2/query_refund");
		Map<String, Object> beanToMap = BeanUtil.beanToMap(model, false, true);
		String xml = XmlUtil.mapToXmlStr(beanToMap);

		ReqLog logreq = new ReqLog(url, xml, null);

		String result = HttpUtil.post(url, xml);
		logreq.setResult(result);

		ZalopayVO jsonObj = JSONObject.parseObject(result, ZalopayVO.class);
		if (jsonObj.isSuccessful()) {
			return new J<>(MsgCodeEnum.SUCCESS, jsonObj, logreq);
		}
		if ("3".equals(jsonObj.getReturn_code()) && jsonObj.getSubReturnCode() >= 1) {
			return new J<>(MsgCodeEnum.SUCCESS, StatusEnum.BETWEENNESS, jsonObj, logreq);
		}
		return new J<>(MsgCodeEnum.FAIL, jsonObj, logreq);
	}
}
